home *** CD-ROM | disk | FTP | other *** search
- .!****************************************************************************
- .!
- .! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
- .!
- .! ** Professional GEM ** by Tim Oren
- .!
- .! Proff File by ST enthusiasts at
- .! Case Western Reserve University
- .! Cleveland, Ohio
- .! uucp : decvax!cwruecmp!bammi
- .! csnet: bammi@case
- .! arpa : bammi%case@csnet-relay
- .! compuserve: 71515,155
- .!
- .!****************************************************************************
- .!
- .!
- .!****************************************************************************
- .!
- .! Begin Part 2
- .!
- .!****************************************************************************
- .!
- .PART II Windows
- .SH EXCELSIOR
- In this installment, we continue the exploration of GEM's window manager by
- finding out how to process the messages received by an application
- when it has a window defined on the screen.
- .PP
- Also, beginning with this column, sample C code demonstrating the techniques
- discussed will be available on SIG*ATARI in DL5. This will allow you to
- download the code without interference by the CIS text-formatter used by ANTIC
- ONLINE output.
- .PP
- The file for this column is GEMCL2.XMO. All references to non-GEM routines in
- this column refer to this file. Please note that these files will not contain
- entire programs. Instead, they consist of small pieces of utility code which
- you may copy and modify in your own programs.
- .SH REDRAWING WINDOWS
- One of the most misunderstood parts of GEM is the correct method for drawing
- within a window. Most requests for redrawing are generated by the GEM system,
- and arrive as messages (read with evnt_multi) which contain the handle of the
- window, and the screen rectangle which is "dirty" and needs to be redrawn.
- .PP
- Screen areas may become dirty as a result of windows being closed, sized down,
- or moved, thus "exposing" an area underneath. The completion of a dialog, or
- closing of a desk accessory may also free up a screen area which needs to be
- redrawn. When GEM detects the presence of a dirty rectangle, it checks its
- list of open windows, and sends the application a redraw message for each of
- its windows which intersects the dirty area.
- .SH CAVEAT EMPTOR
- GEM does not "clip" the rectangle which it sends to the application; that is,
- the rectangle may not lie entirely within the portion of the window which is
- exposed on the screen. It is the job of the application to determine in what
- portion of the rectangle it may safely draw. This is done by examining the
- "rectangle list" associated with the window.
- .PP
- A rectangle list is maintained by GEM for each active window. It contains the
- portions of the window's interior which are exposed, i.e., topmost, on the
- screen and within which the app may draw.
- .PP
- Let's consider an example to make this clear. Suppose an app has opened two
- windows, and there are no desk accessory windows open. The window which is
- topmost will always have only one rectangle in its list. If the two are
- separate on the screen, then the second window will also have one rectangle.
- If they overlap, then the top window will "break" the rectangle of the bottom
- one. If the overlap is at a corner, two rectangles will be generated for the
- bottom window. If the overlap is on a side only, then three rectangles are
- required to cover the exposed portion of the bottom window. Finally, if the
- first window is entirely within the second, it requires four rectangles in the
- list to tile the second window.
- .PP
- Try working out a few rectangle examples with pencil and paper to get the feel
- of it. You will see that the possible combinations with more than two windows
- are enormous. This, by the way, is the reason that GEM does not send one
- message for each rectangle on the list: With multiple windows, the number of
- messages generated would quickly fill up the application's message queue.
- .PP
- Finally, note that every app MUST use this method, even if it only
- uses a single window, because there may be desk accessories with their
- own windows in the system at the same time. If you do not use the
- rectangle lists, you may overwrite an accessory's window.
- .SH INTO THE BITS
- First, we should note that the message type for a redraw request is WM_REDRAW,
- which is stored in msg[0], the first location of the message returned by
- evnt_multi. The window handle is stored in msg[3]. These locations are the
- same for all of the message types being discuss. The rectangle which needs to
- be redrawn is stored in msg[4] through msg[7].
- .PP
- Now let's examine the sample redraw code in more detail. The redraw loop is
- bracketed with mouse off and mouse on calls. If you forget to do this, the
- mouse pointer will be over-written if it is within the window and the next
- movement of the mouse will leave a rectangular blotch on the screen as a piece
- of the "old" screen is incorrectly restored.
- .PP
- The other necessary step is to set the window update flag. This prevents the
- menu manager from dropping a menu on top of the screen portion being redrawn.
- You must release this flag at the end of the redraw, or the you will be unable
- to use any menus afterwards.
- .PP
- The window rectangles are retrieved using a get-first, get-next scheme which
- will be familiar if you have used the GEM DOS or PC-DOS wildcard file calls.
- The end of the rectangle list has been reached when both the width and height
- returned are zero. Since some part of a window might be off-screen
- (unless you have clamped its position - see below), the retrieved
- rectangle is intersected with the desktop's area, and then with the
- screen area for which a redraw was requested.
- .PP
- Now you have the particular area of the screen in which it is legal to draw.
- Unless there is only one window in your application, you will have to test the
- handle in the redraw request to figure out what to put in the rectangle.
- .PP
- Depending on the app, you may be drawing an AES object tree, or executing VDI
- calls, or some combination of the two. In the AES case, the computed
- rectangle is used to specify the bounds of the objc_draw. For VDI
- work, the rectangle is used to set the clipping area before executing
- the VDI calls.
- .SH A SMALL CONFESSION
- At the beginning of this discussion, I deliberately omitted one class of
- redraws: those initiated by the application itself.
- In some cases a part of the screen must be redrawn immediately to give feedback
- to the user following a keystroke, button, or mouse action. In these cases,
- the application could call do_redraw directly, without waiting for a message.
- .PP
- The only time you can bypass do_redraw, and draw without walking the rectangle
- list, is when you can be sure that the target window is on top, and that the
- figure being drawn is entirely contained within it.
- .PP
- In many cases, however, an application initiated redraw happens because of a
- computed change, for instance, a spreadsheet update, and its timing is not
- crucial. In this instance, you may wish to have the app send ITSELF a redraw
- request.
- .PP
- The main advantage of this approach is that the AES is smart enough to see if
- there is already a redraw request for the same window in the queue, and, if so,
- to merge the requests by doing a union of their rectangles. In this fashion,
- the "blinky" appearance of multiple redraws is avoided, without the need to
- include logic for merging redraws within the program.
- A utility routine for sending the "self-redraw" is included in the
- down-load for this article.
- .SH WINDOW CONTROL REQUESTS
- An application is notified by the AES, via the message system, when the user
- manipulates one of the window control points. Remember that you must have
- specified each control point when the window was created, or will not receive
- the associated control message.
- .PP
- The most important thing to understand about window control is that the change
- which the user requested does not take place until the application forwards it
- to the AES. While this makes for a little extra work, it gives the program a
- chance to intervene and validate or modify the request to suit.
- .PP
- A second thing to keep in mind is that not all window updates cause a redraw
- request to be generated for the window, because the AES attempts to save time
- with raster moves on the screen.
- Now let's look at each window control request in detail. The message
- code for a window move is WM_MOVED. If you are willing to accept any
- such request, just do:
- .FB wind_set()
- wind_set(wh, WF_CXYWH, msg[4], msg[5], msg[6], msg[7]);
- .FE
- .sp 1
- .ce 1
- (Remember that wh, the window handle, is always in msg[3]).
- .sp 1
- The AES will not request a redraw of the window following this call, unless the
- window is being moved from a location which is partially "off-screen". Instead,
- it will do a "blit" (raster copy) of the window and its contents to the new
- location without intervention by the app.
- .PP
- There are two constraints which you may often wish to apply to the user's move
- request. The first is to force the new location to lie entirely within the
- desktop, rather than partially off-screen. You can do this with the
- rc_constrain utility by executing:
- .FB rc_constrain()
- rc_constrain(&full, &msg[4]);
- .FE
- before making the wind_set call. (Full is assumed to contain the desktop
- dimensions.)
- .PP
- The second common constraint is to "snap" the x-dimension location of the new
- location to a word boundary. This operation will speed up GEM's "blit" because
- no shifting or masking will need to be done when moving the window. To perform
- this operation, use align() before the wind_set call:
- .FB align()
- msg[4] = align(msg[4], 16);
- .FE
- The message code for a window size request is WM_SIZED. Again, if you are
- willing to accept any request, you can just "turn it around" with the same
- wind_set call as given for WM_MOVED.
- .PP
- Actually, GEM enforces a couple of constraints on sizing. First, the
- window may not be sized off screen. Second, there is a minimum window
- size which is dependent on the window components specified when it was
- created. This prevents features like scroll arrows from being
- squeezed into oblivion. The most common application constraint on
- sizing is to snap the size to horizontal words (as above) and/or
- vertical character lines. In the latter case, the vertical dimension
- of the output font is used with align().
- .PP
- Also, be aware that the size message which you receive specifies the
- EXTERNAL dimensions of the window. To assure an "even" size for the
- INTERNAL dimensions, you must make a wind_calc call to compute them,
- use align() on the computed values, back out the corresponding
- external dimensions with the reverse wind_calc, and then make the
- wind_set call with this set of values.
- .PP
- A window resize will only cause a redraw request for the window if the size is
- being increased in at least one dimension. This is satisfactory for most
- applications, but if you must "reshuffle" the window after a size-down, you
- should send yourself a redraw (as described above) after you make the wind_set
- call. This will guarantee that the display is updated correctly. Also note
- that the sizing or movement of one window may cause redraw requests to be
- generated for other windows which are uncovered by the change.
- .PP
- The window full request, with code WM_FULLED, is actually a toggle. If the
- window is already at its full size (as specified in the wind_create), then this
- is a request to shrink to its previous size. If the window is currently small,
- then the request is to grow to full size.
- .PP
- Since the AES records the current, previous, and maximum window size,
- you can use wind_get calls to determine which situation pertains. The
- hndl_full utility in the down-load (modified from Doodle), shows how
- to do this.
- .PP
- The "zoom box" effects when changing size are optional, and can be removed to
- speed things up. Again, if the window's size is decreasing, no redraw is
- generated, so you must send yourself one if necessary. You should not have to
- perform any constraint or "snap" operations here, since (presumably) the full
- and previous sizes have had these checks applied to them already.
- .PP
- The WM_CLOSED message is received when the close box is clicked. What
- action you perform depends on the application. If you want to remove
- the window, use wind_close as described in the last column. In many
- applications, however, the close message may indicate that a file is
- to be saved, or a directory or editing level is to be closed. In
- these cases, the message is used to trigger this action before or
- instead of the wind_close. (Folders on the Desktop are an example of
- this situation.)
- .PP
- The WM_TOPPED message indicates that the AES wants to bring the
- indicated window to the "top" and make it active. This happens if the
- user clicks within a window which is not on top, or if the currently
- topped window is closed by its application or desk accessory.
- Normally, the application should respond to this message with:
- .FB wind_set()
- wind_set(wh, WF_TOP, 0, 0);
- .FE
- and allow the process to complete.
- .PP
- In a few instances, a window may be used in an output only mode, such as a
- status display, with at least one other window present for input. In
- this case, a WM_TOPPED message for the status window may be ignored.
- In all other cases, you must handle the WM_TOPPED message even if your
- application has only one window: Invocation of a desk accessory could
- always place another window on top. If you fail to do so, subsequent
- redraws for your window may not be processed correctly.
- .SH WINDOW SLIDER MESSAGES
- If you specify all of the slider bar parts for your window, you may receive up
- to five different message types for each of the two sets of sliders. To
- simplify things a little, I will discuss everything in terms of the vertical
- (right hand side) sliders. If you are also using the horizontal sliders, the
- same techniques will work, just use the alternate mnemonics.
- .PP
- The WM_VSLID message indicates that the user has dragged the slider bar within
- its box, indicating a new relative position within the document.
- Along with the window handle, this message includes the relative
- position between 1 and 1000 in msg[4].
- .PP
- Recall from last column's discussion that this interval corresponds to the
- "freedom of movement" of the slider. If you want to accept the user's request,
- just make the call:
- .FB wind_set
- wind_set(wh, WF_VSLIDE, msg[4], 0, 0, 0);
- .FE
- .sp 1
- .ce 1
- (Corresponding horizontal mnemonics are WM_HSLID and WF_HSLIDE).
- .sp 1
- Note that this wind_set call will not cause a redraw message to be sent. You
- must update the display to reflect the new scrolled position, either by
- executing a redraw directly, or by sending yourself a message.
- .PP
- If the document within the window has some structure, you may not wish
- to accept all slider positions. Instead you may want to force the
- scroll position to the nearest text line (for instance). Using terms
- defined in the last column, you may convert the slider position to
- "document units" with:
- .sp 1
- .ce 1
- top_wind = msg[4] * (total_doc - seen_doc) / 1000 + top_doc
- .sp 1
- .ce 1
- (This will probably require 32-bit arithmetic).
- .sp 1
- After rounding off or otherwise modifying the request, convert it back
- to slider units and make the WF_VSLIDE request.
- .PP
- The other four slider requests all share one message code: WM_ARROWED.
- They are distinguished by sub-codes stored in msg[4]: WA_UPPAGE,
- WA_DNPAGE, WA_UPLINE, and WA_DNLINE. These are produced by clicking
- above and below the slider, and on the up and down arrows,
- respectively. (I have no idea why sub-codes were used in this one
- instance.) The corresponding horizontal slider codes are:
- WA_LFPAGE, WA_RTPAGE, WA_LFLINE, and WA_RTLINE.
- .PP
- What interpretation you give to these requests will depend on the application.
- In the most common instance, text documents, the customary method is to change
- the top of window position (top_wind) by one line for a WA_UPLINE or
- WA_DNLINE, and by seen_doc (the number of lines in the window) for a
- WA_UPPAGE or WA_DNPAGE.
- .PP
- After making the change, compute a new slider position, and make the wind_set
- call as given above. If the document's length is not an even multiple of
- "lines" or "pages" you will have to be careful that incrementing or
- decrementing top_wind does not exceed its range of freedom: top_doc to (top_doc
- + total_doc - seen_doc).
- .PP
- If you have such an odd size document, you will also have to make a decision on
- whether to violate the line positioning rule so that the slider may be put at
- its bottom-most position, or to follow the rule but make it impossible to get
- the slider to the extreme of its range.
- .SH A COMMON BUG
- It is easy to forget that user clicks are not the only things that
- affect slider position. If the window size changes as a result of a
- WM_SIZED or WM_FULLED message, the app must also update its sliders
- (if they are present). This is a good reason to keep the top of
- window information in "document units".
- .PP
- You can just redo the position calculation with the new "seen_doc" value, and
- call wind_set. Also remember that changing the size of the
- underlying document (adding or deleting a bottom line, for instance)
- must also cause the sliders to be adjusted.
- .SH DEPT. OF DIRTY TRICKS
- There are two remaining window calls which are useful to advanced programmers.
- They require techniques which I have not yet discussed, so you may need to file
- them for future reference.
- .PP
- The AES maintains a quarter-screen sized buffer which is used to save the area
- under alerts and menu drop-downs. It is occasionally useful for the
- application to gain access to this buffer for its own use in saving
- screen areas with raster copies. To do so, use:
- .FB wind_get()
- wind_get(0, WF_SCREEN, &loaddr, &hiaddr, &lolen, &hilen);
- .FE
- Hiaddr and loaddr are the top and bottom 16-bits (respectively) of the 32-bit
- address of the buffer. Hilen and lolen are the two halves of its length.
- .PP
- Due to a preculiarity of the binding you have to reassemble these
- pieces before using them. (The actual value of WF_SCREEN is 17; this
- does not appear in some versions of the GEMDEFS.H file.)
- .PP
- If you use this buffer, you MUST prevent menus from dropping down by using
- either the BEG_UPDATE or BEG_MCTRL wind_update calls. Failure to do so will
- result in your data being destroyed. Remember to use the matching wind_update:
- END_UPDATE or END_MCTRL, when you are done.
- .PP
- The other useful call enables you to replace the system's desktop definition
- with a resource of your choosing. The call:
- .FB wind_set()
- wind_set(0, WF_NEWDESK, tree, 0, 0);
- .FE
- where tree is the 32-bit address of the object tree, will cause the AES to draw
- your definition instead of the usual gray or green background. Not
- only that, it will continue to redraw this tree with no intervention
- on your part.
- .PP
- Obviously, the new definition must be carefully built to fit the desktop area
- exactly or garbage will be left around the edges. For the truly sophisticated,
- a user-defined object could be used in this tree, with the result that your
- application's code would be entered from the AES whenever the desktop was
- redrawn. This would allow you to put VDI pictures or complex images onto the
- desktop background.
- .SH A SIN OF OMISSION
- In the last column, I neglected to mention that strings whose addresses are
- passed in the WF_NAME and WF_INFO wind_set calls must be allocated in a static
- data area. Since the AES remembers the addresses (not the characters), a
- disaster may result if the storage has been reused when the window manager next
- attempts to draw the window title area.
- .SH COMING SOON
- This concludes our tour of GEM's basic window management techniques. There have
- been some unavoidable glimpses of paths not yet taken (forward references), but
- we will return in time.
- .PP
- On our next excursion, we will take a look at techniques for handling simple
- dialog boxes, and start exploring the mysteries of resources and object trees.
- .!
- .!
- .!*****************************************************************************
- .!* *
- .!* End Part 2 *
- .!* *
- .!*****************************************************************************
-